package com.rogo.net
{
	import com.rogo.events.SocketPackageEvent;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	
	[Event(name="socketConnect", type="com.rogo.events.SocketPackageEvent")]
	
	[Event(name="socketClose", type="com.rogo.events.SocketPackageEvent")]
	
	[Event(name="socketError", type="com.rogo.events.SocketPackageEvent")]
	
	[Event(name="packageReceived", type="com.rogo.events.SocketPackageEvent")]
	
	[Event(name="packageSend", type="com.rogo.events.SocketPackageEvent")]
	
	/**
	 * Socket数据包发送接收类
	 * 
	 * @author Rogo
	 */	
	public class SocketProxy extends EventDispatcher
	{
		public function SocketProxy()
		{
			super();
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Variables
		//
		//--------------------------------------------------------------------------
		
		/**
		 * 主机 
		 */		
		public var host:String;
		
		/**
		 * 端口 
		 */		
		public var port:int;
		
		/**
		 * 是否已连接
		 */		
		public function get connected():Boolean
		{
			return socket && socket.connected;
		}
		
		
		private var socket:Socket;
		private var connecting:Boolean = false;
		private var pendingData:Array = [];
		private var cacheData:ByteArray = new ByteArray();
		
		/**
		 * 正在读取的数据包 
		 */		
		private var readingPackage:IPackage;
		
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 * 发送数据包
		 * 
		 * @param value
		 */		
		public function send(value:IPackage):void
		{
			if (!connected)
			{
				pendingData.push(value);
				connect();
			}
			else
				sendPackage(value);
		}
		
		/**
		 * 连接到指定主机端口
		 */		
		public function connect():void
		{
			if (connected || connecting)
				return;
			
			if (!socket)
				initSocket();
			
			connecting = true;
			socket.connect(host, port);
		}
		
		private function initSocket():void
		{
			socket = new Socket();
			socket.addEventListener(Event.CONNECT, socket_connectHandler);
			socket.addEventListener(Event.CLOSE, socket_closeHandler);
			socket.addEventListener(IOErrorEvent.IO_ERROR, socket_ioErrorHandler);
			socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, socket_securityErrorHandler);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, socket_dataHandler);
		}
		
		/**
		 * 发送socket数据包
		 * 
		 * @param value
		 */		
		private function sendPackage(value:IPackage):void
		{
			if (connected)
			{
				var bytes:ByteArray = new ByteArray();
				bytes.writeBytes(value.head);
				bytes.writeBytes(value.body);
				bytes.position = 0;
				
				var event:SocketPackageEvent = new SocketPackageEvent(SocketPackageEvent.PACKAGE_SEND);
				event.data = value;
				dispatchEvent(event);
				
				socket.writeBytes(bytes);
				socket.flush();
			}
		}
		
		/**
		 * 从socket读取的缓存数据中读取包头信息 并返回一个含有这个包头的数据包
		 * 
		 * @return 
		 */		
		public function readPackageHead(bytes:ByteArray):IPackage
		{
			// override method
			return null;
		}
		
		/**
		 * 读取数据包
		 */		
		protected function readPackage():void
		{
			if (!readingPackage)
			{
				readingPackage = readPackageHead(cacheData);
				
				if (!readingPackage)
					return;
			}
			
			// 缓存的数据不足一个包的长度
			if (cacheData.bytesAvailable < readingPackage.bodyLength)
				return;
			
			// 读取包体数据
			var body:ByteArray = new ByteArray();
			body.writeBytes(cacheData, 0, readingPackage.bodyLength);
			readingPackage.body = body;
			
			// 处理读取的数据包
			processPackage(readingPackage);
			
			// 如果缓存区还有有效数据 继续读包 
			if (cacheData.bytesAvailable)
				readPackage();
			else
				cacheData.clear();
		}
		
		/**
		 * 缓存socket接收到的数据
		 */		
		private function cacheSocketData(data:ByteArray):void
		{
			// 将数据缓存到缓存数据的尾部
			// [...] <<< data
			var curPosition:uint = cacheData.position;
			cacheData.position = cacheData.length;
			cacheData.writeBytes(data);
			cacheData.position = curPosition;
		}
		
		/**
		 * 处理解析出来的包
		 * 
		 * @param recPackage
		 */		
		private function processPackage(readPackage:IPackage):void
		{
			var event:SocketPackageEvent = new SocketPackageEvent(SocketPackageEvent.PACKAGE_RECEIVED);
			event.data = readPackage;
			dispatchEvent(event);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Event Handlers
		//
		//--------------------------------------------------------------------------
		
		private function socket_connectHandler(e:Event):void
		{
			connecting = false;
			dispatchEvent(new SocketPackageEvent(SocketPackageEvent.SOCKET_CONNECT));
			
			// 发送待发送的数据包
			while (pendingData.length > 0)
			{
				var pendingPackage:IPackage = pendingData.shift();
				sendPackage(pendingPackage);
			}
		}
		
		private function socket_closeHandler(e:Event):void
		{
			dispatchEvent(new SocketPackageEvent(SocketPackageEvent.SOCKET_CLOSE));
		}
		
		private function socket_ioErrorHandler(e:IOErrorEvent):void
		{
			connecting = false;
			dispatchEvent(new SocketPackageEvent(SocketPackageEvent.SOCKET_ERROR));
		}
		
		private function socket_securityErrorHandler(e:SecurityErrorEvent):void
		{
			connecting = false;
			dispatchEvent(new SocketPackageEvent(SocketPackageEvent.SOCKET_ERROR));
		}
		
		/**
		 * Socket数据处理
		 * 
		 * @param event
		 */ 
		private function socket_dataHandler(e:ProgressEvent):void
		{
			while (socket.bytesAvailable)
			{
				// 缓存接收的数据
				var buffer:ByteArray = new ByteArray();
				socket.readBytes(buffer);
				cacheSocketData(buffer);
			}
			
			// 开始读包
			readPackage();
		}
		
	}
}